﻿using System;
using System.Collections.ObjectModel;
using log4net;
using Microsoft.Practices.Prism.Events;
using Reactor.Environment;
using Reactor.Messages.Events.Failures;
using Reactor.MiniManager.Events;
using Reactor.MiniManager.Extensions;
using Reactor.MiniManager.ViewModels;
using Reactor.MiniManager.Workflows;
using Reactor.Providers;
using Reactor.Resources;
using Samurai.Wakizashi;
using Samurai.Wakizashi.Workflow;

namespace Reactor.MiniManager.State
{
    public interface ICoreStateCache : IDisposable
    {
        /// <summary>
        /// Initializes this instance by obtaining an initial list of expected Cores and Services.
        /// </summary>
        void Initialize();

        /// <summary>
        /// Gets a bindable collection of <seealso cref="ReactorCoreViewModel"/>. This collection is 
        /// never updated by any thread but the UI thread.
        /// </summary>
        /// <value>The reactor core view models.</value>
        ObservableCollection<ReactorCoreViewModel> ReactorCoreViewModels { get; }

        /// <summary>
        /// Gets or sets the interval at which the state cache performs health checks on registered Cores.
        /// </summary>
        /// <value>The health check interval.</value>
        TimeSpan HealthCheckInterval { get; set; }

        /// <summary>
        /// Signals the state cache to susbscribe to service health reports.
        /// </summary>
        void SubscribeToServiceNotRespondingEvents();

        /// <summary>
        /// Starts the workflow to perform regular service health checks.
        /// </summary>
        void StartCoreHealthCheckWorkflow();

        /// <summary>
        /// Gets or sets the workflow responsible for periodically checking the health 
        /// of the services identified by the cache.
        /// </summary>
        /// <value>The health check workflow.</value>
        ISequentialWorkflow<CoreHealthCheckWorkflow.WorkflowContext> CoreHealthCheckWorkflow { get; set; }

        /// <summary>
        /// Gets or sets the workflow responsible for initializing the ICoreStateCache instance.
        /// </summary>
        /// <value>The initialization workflow.</value>
        ISequentialWorkflow<CoreStateCacheInitializationWorkflow.WorkflowContext> InitializationWorkflow { get; set; }

        /// <summary>
        /// Clears the cache.
        /// </summary>
        void Clear();
    }

    public class CoreStateCache : DisposableBase, ICoreStateCache
    {
        #region Fields

        private static readonly ILog Log = LogManager.GetLogger(typeof(ICoreStateCache));
        private readonly ICoreDataProvider _coreDataProvider;
        private readonly IEventAggregator _eventAggregator;

        #endregion

        #region Constructors

        /// <summary>
        /// Initializes a new instance of the <see cref="CoreStateCache"/> class.
        /// </summary>
        /// <param name="coreDataProvider">The core data provider.</param>
        /// <param name="eventAggregator">The event aggregator.</param>
        public CoreStateCache(ICoreDataProvider coreDataProvider, IEventAggregator eventAggregator)
        {
            if (coreDataProvider == null) throw new ArgumentNullException("coreDataProvider");
            if (eventAggregator == null) throw new ArgumentNullException("eventAggregator");

            HealthCheckInterval = ObtainHealthCheckInterval();
            ReactorCoreViewModels = new ObservableCollection<ReactorCoreViewModel>();

            _coreDataProvider = coreDataProvider;
            _eventAggregator = eventAggregator;

            if (Log.IsDebugEnabled) Log.Debug("CoreStateCache created");
        }

        #endregion

        #region Public Methods

        /// <summary>
        /// Initializes this instance by obtaining an initial list of expected Cores and Services.
        /// </summary>
        public void Initialize()
        {
            // Create workflow and kick off workflow to obtain expected Cores and Services
            if (InitializationWorkflow == null)
                InitializationWorkflow = new CoreStateCacheInitializationWorkflow(_coreDataProvider, this);

            InitializationWorkflow.WorkflowCompleted += InitializationWorkflowWorkflowCompleted;
            InitializationWorkflow.WorkflowFailed += InitializationWorkflowWorkflowFailed;
            InitializationWorkflow.Start();
        }

        /// <summary>
        /// Clears the cache.
        /// </summary>
        public void Clear()
        {
            this.ExecuteOnUiThread(() => ReactorCoreViewModels.Clear());
        }

        /// <summary>
        /// Signals the state cache to susbscribe to service health reports.
        /// </summary>
        public void SubscribeToServiceNotRespondingEvents()
        {
            _eventAggregator.GetEvent<ServiceNotRespondingEventReceived>().Subscribe(ProcessServiceNotRespondingEvent);
        }

        /// <summary>
        /// Starts the workflow to perform regular service health checks.
        /// </summary>
        public void StartCoreHealthCheckWorkflow()
        {
            if (CoreHealthCheckWorkflow == null)
                CoreHealthCheckWorkflow = new CoreHealthCheckWorkflow(HealthCheckInterval, ReactorCoreViewModels, _coreDataProvider);

            CoreHealthCheckWorkflow.Start();
        }

        #endregion

        #region Public Properties

        /// <summary>
        /// Gets a bindable collection of <seealso cref="ReactorCoreViewModel"/>. This collection is 
        /// never updated by any thread but the UI thread.
        /// </summary>
        /// <value>The reactor core view models.</value>
        public ObservableCollection<ReactorCoreViewModel> ReactorCoreViewModels { get; private set; }

        /// <summary>
        /// Gets or sets the interval at which the state cache performs health checks on registered Cores.
        /// </summary>
        /// <value>The health check interval.</value>
        public TimeSpan HealthCheckInterval { get; set; }

        /// <summary>
        /// Gets or sets the workflow responsible for initializing the ICoreStateCache instance.
        /// </summary>
        /// <value>The initialization workflow.</value>
        public ISequentialWorkflow<CoreStateCacheInitializationWorkflow.WorkflowContext> InitializationWorkflow { get; set; }

        /// <summary>
        /// Gets or sets the workflow responsible for periodically checking the health
        /// of the services identified by the cache.
        /// </summary>
        /// <value>The health check workflow.</value>
        public ISequentialWorkflow<CoreHealthCheckWorkflow.WorkflowContext> CoreHealthCheckWorkflow { get; set; }

        #endregion

        #region Private Methods

        /// <summary>
        /// Processes the service health report by updating the internal cache 
        /// with online statuses. This method always executes on the UI thread since it 
        /// can cause UI updates.
        /// </summary>
        private void ProcessServiceNotRespondingEvent(ServiceNotResponding notRespondingEvent)
        {
            this.ExecuteOnUiThread(() =>
            {
                
            });
        }

        private void InitializationWorkflowWorkflowFailed(object sender, WorkflowCompletedEventArgs<CoreStateCacheInitializationWorkflow.WorkflowContext> e)
        {
            // Detach workflow events
            var workflow = (ISequentialWorkflow<CoreStateCacheInitializationWorkflow.WorkflowContext>)sender;
            workflow.WorkflowCompleted -= InitializationWorkflowWorkflowCompleted;
            workflow.WorkflowFailed -= InitializationWorkflowWorkflowFailed;

            // Handle Failure
            //TODO: Send failure message

            InitializationWorkflow = null;
        }

        private void InitializationWorkflowWorkflowCompleted(object sender, WorkflowCompletedEventArgs<CoreStateCacheInitializationWorkflow.WorkflowContext> e)
        {
            // Detach workflow events
            var workflow = (ISequentialWorkflow<CoreStateCacheInitializationWorkflow.WorkflowContext>)sender;
            workflow.WorkflowCompleted -= InitializationWorkflowWorkflowCompleted;
            workflow.WorkflowFailed -= InitializationWorkflowWorkflowFailed;

            InitializationWorkflow = null;
        }

        private static TimeSpan ObtainHealthCheckInterval()
        {
            var minutes = 5;

            // Attempt read of configuration value
            var configValue = Context.Configuration.GetConfigurationValue(CommonResources.ConfigKey_ServiceHealthIntervalException);
            if (string.IsNullOrEmpty(configValue))
            {
                if (Log.IsWarnEnabled) Log.Warn(string.Format("No configuration value found at '{0}'. Using default of five minutes.", CommonResources.ConfigKey_ServiceHealthIntervalException));
                return TimeSpan.FromMinutes(minutes);
            }

            // Attempt to parse and return configuration value
            if (int.TryParse(configValue, out minutes))
                return TimeSpan.FromMinutes(minutes);

            // Couldnt parse configuration value
            if (Log.IsWarnEnabled) Log.Warn(string.Format("Unable to parse configuration value found at '{0}'. Using default of five minutes.", CommonResources.ConfigKey_ServiceHealthIntervalException));
            return TimeSpan.FromMinutes(minutes);
        }

        #endregion

        /// <summary>
        /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
        /// </summary>
        protected override void Dispose(bool disposing)
        {
            // Shutdown workflows
            if (CoreHealthCheckWorkflow != null)
                CoreHealthCheckWorkflow.RequestStop();

            base.Dispose(disposing);
        }
    }
}
